<?php
/**
 * Created by PhpStorm.
 * User: skj19
 * Date: 2016/5/24
 * Time: 14:33
 */

namespace ClassLibrary;

/**
 * 地理位置经纬度hash
 * Class ClGeoHash
 * @package ClassLibrary
 */
class ClGeoHash {

    private static $table = "0123456789bcdefghjkmnpqrstuvwxyz";

    private static $bits = array(
        0b10000, 0b01000, 0b00100, 0b00010, 0b00001
    );

    /**
     * 编码
     * @param $lng
     * @param $lat
     * @param float $prec
     * @return string
     */
    public static function encode($lng, $lat, $prec = 0.00001) {
        $minlng = -180;
        $maxlng = 180;
        $minlat = -90;
        $maxlat = 90;
        $hash   = array();
        $error  = 180;
        $isEven = true;
        $chr    = 0b00000;
        $b      = 0;
        while ($error >= $prec) {
            if ($isEven) {
                $next = ($minlng + $maxlng) / 2;
                if ($lng > $next) {
                    $chr    |= self::$bits[$b];
                    $minlng = $next;
                } else {
                    $maxlng = $next;
                }
            } else {
                $next = ($minlat + $maxlat) / 2;
                if ($lat > $next) {
                    $chr    |= self::$bits[$b];
                    $minlat = $next;
                } else {
                    $maxlat = $next;
                }
            }
            $isEven = !$isEven;
            if ($b < 4) {
                $b++;
            } else {
                $hash[] = self::$table[$chr];
                $error  = max($maxlng - $minlng, $maxlat - $minlat);
                $b      = 0;
                $chr    = 0b00000;
            }
        }
        return join('', $hash);
    }

    public static function expand($hash) {
        list($minlng, $maxlng, $minlat, $maxlat) = self::decode($hash);
        $dlng = ($maxlng - $minlng) / 2;
        $dlat = ($maxlat - $minlat) / 2;
        return array(
            self::encode($minlng - $dlng, $maxlat + $dlat),
            self::encode($minlng + $dlng, $maxlat + $dlat),
            self::encode($maxlng + $dlng, $maxlat + $dlat),
            self::encode($minlng - $dlng, $maxlat - $dlat),
            self::encode($maxlng + $dlng, $maxlat - $dlat),
            self::encode($minlng - $dlng, $minlat - $dlat),
            self::encode($minlng + $dlng, $minlat - $dlat),
            self::encode($maxlng + $dlng, $minlat - $dlat),
        );
    }

    public static function getRect($hash) {
        list($minlng, $maxlng, $minlat, $maxlat) = self::decode($hash);
        return array(
            array($minlng, $minlat),
            array($minlng, $maxlat),
            array($maxlng, $maxlat),
            array($maxlng, $minlat),
        );
    }

    /**
     * 解码
     * decode a geohash string to a geographical area
     * @var $hash string geohash
     * @return array array($minlng, $maxlng, $minlat, $maxlat);
     */
    public static function decode($hash) {
        $minlng = -180;
        $maxlng = 180;
        $minlat = -90;
        $maxlat = 90;
        for ($i = 0, $c = strlen($hash); $i < $c; $i++) {
            $v = strpos(self::$table, $hash[$i]);
            if (1 & $i) {
                if (16 & $v) {
                    $minlat = ($minlat + $maxlat) / 2;
                } else {
                    $maxlat = ($minlat + $maxlat) / 2;
                }
                if (8 & $v) {
                    $minlng = ($minlng + $maxlng) / 2;
                } else {
                    $maxlng = ($minlng + $maxlng) / 2;
                }
                if (4 & $v) {
                    $minlat = ($minlat + $maxlat) / 2;
                } else {
                    $maxlat = ($minlat + $maxlat) / 2;
                }
                if (2 & $v) {
                    $minlng = ($minlng + $maxlng) / 2;
                } else {
                    $maxlng = ($minlng + $maxlng) / 2;
                }
                if (1 & $v) {
                    $minlat = ($minlat + $maxlat) / 2;
                } else {
                    $maxlat = ($minlat + $maxlat) / 2;
                }
            } else {
                if (16 & $v) {
                    $minlng = ($minlng + $maxlng) / 2;
                } else {
                    $maxlng = ($minlng + $maxlng) / 2;
                }
                if (8 & $v) {
                    $minlat = ($minlat + $maxlat) / 2;
                } else {
                    $maxlat = ($minlat + $maxlat) / 2;
                }
                if (4 & $v) {
                    $minlng = ($minlng + $maxlng) / 2;
                } else {
                    $maxlng = ($minlng + $maxlng) / 2;
                }
                if (2 & $v) {
                    $minlat = ($minlat + $maxlat) / 2;
                } else {
                    $maxlat = ($minlat + $maxlat) / 2;
                }
                if (1 & $v) {
                    $minlng = ($minlng + $maxlng) / 2;
                } else {
                    $maxlng = ($minlng + $maxlng) / 2;
                }
            }
        }
        return array($minlng, $maxlng, $minlat, $maxlat);
    }

}